<template>
  <div class="mind-box" @click="closeSearchAndReplace">
    <div class="mindContainer" :id="`mindContainer_${mapKey}`" v-resize="setMindMapResize"></div>
    <fix-tabor
        v-if="!props.readonly"
        :selected="activeNodes.length > 0"
        :nodes-length="activeNodes.length + ((activeNodes[0] && activeNodes[0].children) ? activeNodes[0].children.length : 0)"
        :select-root="hasRoot"
        :is-start="isStart"
        :is-end="isEnd"
        @clickTab="handleTabor"
    />
    <insert-image-com
        v-model:visible="imageVisible"
        @uploadImage="insertImage"
    />
    <insert-emoji-com v-model:visible="emojiVisible" @setEmoji="insertEmoji"/>
    <insert-link-com
        v-model:visible="linkVisible"
        @insertLink="insertLink"
        :default-data="linkData"
    />
    <insert-formula
        v-model:visible="officialVisible"
        @insert-formula="insertOfficial"
    />
    <set-node-style
        v-model:visible="styleVisible"
        :node-style="noteStyle"
        @set-style="handleStyle"
    />
    <export-selector v-model:visible="exportVisible" @confirm="handleExport"/>
    <div
        class="el-notification right"
        style="top: 16px; width: 330px; z-index: 2280"
        v-if="searchVisible"
        @click.stop="() => {}"
    >
      <div class="el-notification__group">
        <div class="el-notification__content">
          <el-input
              v-model="searchValue"
              placeholder="搜索"
              @keydown.enter="handleSearch($event)"
              class="w-260"
          >
            <template #suffix>
              <span v-if="searchTotal > 0"
              >{{ searchIndex }}/{{ searchTotal }}</span
              >
            </template>
          </el-input>
        </div>
        <el-icon
            class="el-icon el-notification__closeBtn"
            @click="closeSearchAndReplace"
        >
          <component is="Close"></component>
        </el-icon>
      </div>
    </div>
    <div
        class="el-notification right"
        style="top: 16px; z-index: 2280"
        v-if="replaceVisible"
        @click.stop="() => {}"
    >
      <div class="el-notification__group">
        <div class="el-notification__content">
          <el-input
              v-model="searchValue"
              placeholder="搜索"
              class="w-260"
              @keydown.enter="handleSearch($event)"
          >
            <template #suffix>
              <span v-if="searchTotal > 0"
              >{{ searchIndex }}/{{ searchTotal }}</span
              >
            </template>
          </el-input>
          <el-button class="margin-left-8" @click="handleReplace('click')"
          >替换
          </el-button
          >
          <div class="h-10"></div>
          <el-input
              v-model="replaceValue"
              placeholder="替换"
              class="w-260"
              @keydown.enter="handleReplace($event)"
          ></el-input>
          <el-button class="margin-left-8" @click="handleReplace('click', true)"
          >全部替换
          </el-button
          >
        </div>
        <el-icon
            class="el-icon el-notification__closeBtn"
            @click="closeSearchAndReplace"
        >
          <component is="Close"></component>
        </el-icon>
      </div>
    </div>
    <div
        class="node-menu"
        v-if="nodeMenu.show"
        :style="{ left: `${nodeMenu.left}px`, top: `${nodeMenu.top}px` }"
        @contextmenu="doNothing($event)"
    >
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('link_note')"
      >
        <span>关联笔记</span>
        <span class="keyboard"> </span>
      </div>
      <div
          class="flex justify-content-between item"
          :class="nodeMenu.isRoot ? 'disable' : ''"
          @click.stop="handleExec('insert_node')"
      >
        <span>插入同级节点</span>
        <span class="keyboard">{{ isMac ? 'Return' : 'Enter' }}</span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('insert_child_node')"
      >
        <span>插入子节点</span>
        <span class="keyboard">Tab</span>
      </div>
      <div
          class="flex justify-content-between item"
          :class="nodeMenu.isRoot ? 'disable' : ''"
          @click.stop="handleExec('insert_parent_node')"
      >
        <span>插入父节点</span>
        <span class="keyboard">Shift + Tab</span>
      </div>
      <div
          class="flex justify-content-between item"
          :class="nodeMenu.isRoot ? 'disable' : ''"
          @click.stop="handleExec('up_node')"
      >
        <span>上移节点</span>
        <span class="keyboard">{{ command }} + ↑</span>
      </div>
      <div
          class="flex justify-content-between item"
          :class="nodeMenu.isRoot ? 'disable' : ''"
          @click.stop="handleExec('down_node')"
      >
        <span>下移节点</span>
        <span class="keyboard">{{ command }} + ↓</span>
      </div>
      <div
          class="flex justify-content-between item danger"
          :class="nodeMenu.isRoot ? 'disable' : ''"
          @click.stop="handleExec('remove_node')"
      >
        <span>删除节点</span>
        <span class="keyboard">Delete</span>
      </div>
      <div
          class="flex justify-content-between item danger"
          :class="nodeMenu.isRoot ? 'disable' : ''"
          @click.stop="handleExec('remove_current_node')"
      >
        <span>仅删除当前节点</span>
        <span class="keyboard">Shift + Backspace</span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('copy_node')"
      >
        <span>复制节点</span>
        <span class="keyboard">{{ command }} + C</span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('cut_node')"
      >
        <span>剪切节点</span>
        <span class="keyboard">{{ command }} + X</span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('paste_node')"
      >
        <span>粘贴节点</span>
        <span class="keyboard">{{ command }} + V</span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('remove_custom_styles')"
      >
        <span>一键去除自定义样式</span>
        <span class="keyboard"> </span>
      </div>
    </div>
    <div
        class="node-menu"
        v-if="bgMenu.show"
        :style="{ left: `${bgMenu.left}px`, top: `${bgMenu.top}px` }"
        @contextmenu="doNothing($event)"
    >
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('return_center')"
      >
        <span>回到根节点</span>
        <span class="keyboard"
        >{{ command }} + {{ isMac ? 'Return' : 'Enter' }}</span
        >
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('expand_all')"
      >
        <span>展开所有</span>
        <span class="keyboard"> </span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('unexpand_all')"
      >
        <span>收起所有</span>
        <span class="keyboard"> </span>
      </div>
      <!--      <div class="flex justify-content-between item" @click.stop="handleExec('reset_layout')">
              <span>一键整理布局</span>
              <span class="keyboard">{{ command }} + L</span>
            </div>-->
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('fit_canvas')"
      >
        <span>适应画布</span>
        <span class="keyboard">{{ command }} + i</span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="handleExec('remove_all_node_custom_styles')"
      >
        <span>一键去除所有节点自定义样式</span>
        <span class="keyboard"> </span>
      </div>
      <div
          class="flex justify-content-between item"
          @click.stop="copyToClipboard"
      >
        <span>复制图片到剪贴板</span>
        <span class="keyboard"> </span>
      </div>
    </div>
    <el-dialog
        v-model="linkNoteVisible"
        draggable
        :close-on-click-modal="true"
        :title="`关联笔记`"
        center
        width="400"
    >
      <div class="h-10"></div>
      <el-tree-select
          filterable
          v-model="insertNodeValue"
          :data="selectNodeTree"
          :render-after-expand="false"
          node-key="id"
          clearable
      >
        <template #default="{ node, data }">
        <span>
          <el-icon>
            <FlowChart v-if="data.icon === 'FlowChart'"/>
            <Xmind v-else-if="data.icon === 'Xmind'"/>
            <Document v-else-if="data.icon === 'Document'"/>
            <Folder v-else/>
          </el-icon>
          {{ node.label }}
        </span>
        </template>
      </el-tree-select>
      <div class="h-30"></div>
      <template #footer>
        <el-button type="primary" @click="confirmInsertNode">确 定</el-button>
        <el-button type="warning" @click="linkNoteVisible = false"
        >取 消
        </el-button
        >
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import MindMap from "simple-mind-map"
import FixTabor from "./FixTabor.vue"
import plugins, {defaultNoteStyle} from './plugin.js'
import {debounce} from "@/utils/tools.ts"
import {onMounted, shallowRef, computed, ref, reactive, nextTick, watch, onUnmounted} from 'vue'
import {ElInput, ElMessage, ElMessageBox} from "element-plus"
import iconList from "@/components/mind-map/IconsConfig.js"
import InsertImageCom from '@/components/mind-map/InsertImage.vue'
import InsertEmojiCom from '@/components/mind-map/InsertEmoji.vue'
import InsertLinkCom from '@/components/mind-map/InsertLink.vue'
import InsertFormula from '@/components/mind-map/InsertFormula.vue'
import SetNodeStyle from '@/components/mind-map/SetNodeStyle.vue'
import ExportSelector from '@/components/mind-map/ExportSelector.vue'
import {imgToDataUrl} from 'simple-mind-map/src/utils'
import {useRoute} from "vue-router"
import useNoteStore from "@/stores/note.js"
import {Folder, Document} from "@element-plus/icons-vue"
import Xmind from "@/icons/Xmind.vue"
import FlowChart from "@/icons/FlowChart.vue"
import { getHost, getIconPath, getNoteNameAndIcon, isObjectJson } from '@/utils/index.js'

const route = useRoute()
const noteStore = useNoteStore()

const props = defineProps({
  data: Object,
  readonly:{
    type:Boolean,
    default:false
  },
  needLeft: {
    type: Boolean,
    default: false
  },
  mapKey: {
    type: String,
    default: ''
  },
})

/**
 * @type {MindMap}
 */
let mmRef = null
const emits = defineEmits(['update:data', 'change'])
const isMac = computed(() => {
  const agent = navigator.userAgent.toLowerCase()
  return /macintosh|mac os x /i.test(agent)
})
const command = isMac.value ? '⌘' : 'Ctrl'
const activeNodes = shallowRef([])
const initWidth = ref(0)

const isStart = ref(true)
const isEnd = ref(true)
const imageVisible = ref(false)
const emojiVisible = ref(false)
const linkVisible = ref(false)
const officialVisible = ref(false)
const styleVisible = ref(false)
const exportVisible = ref(false)
const noteStyle = reactive({...defaultNoteStyle})
const searchValue = ref('')
const replaceValue = ref('')
const searchVisible = ref(false)
const replaceVisible = ref(false)
const searchIndex = ref(0)
const searchTotal = ref(0)
const linkNoteVisible = ref(false)
const insertNodeValue = ref('')
const nodeMenu = reactive({
  node: null,
  left: 0,
  top: 0,
  show: false,
  isRoot: false,
})
const bgMenu = reactive({
  left: 0,
  top: 0,
  show: false,
})
const linkData = reactive({
  url: '',
  name: '',
})

const rootUpdate = ref(false)

const doNothing = (event) => {
  event.preventDefault()
}

watch(
    () => props.data,
    (val) => {
      if (mmRef !== null) {
        rootUpdate.value = true
        const fullData = mmRef.getData(true)
        mmRef.setFullData({...fullData, root: val})
        nextTick(() => {
          rootUpdate.value = false
        })
      }
    },
    {deep: true}
)

onMounted(() => {
  initWidth.value = document.getElementById('mindContainer_' + props.mapKey).offsetWidth
  handleInit()
  // MindMap.usePlugin(plugins.Watermark)
  MindMap.usePlugin(plugins.OuterFrame)
  MindMap.usePlugin(plugins.NodeImgAdjust)
  MindMap.usePlugin(plugins.RichText)
  MindMap.usePlugin(plugins.Formula)
  MindMap.usePlugin(plugins.Export)
  MindMap.usePlugin(plugins.ExportPDF)
  MindMap.usePlugin(plugins.ExportXMind)
  MindMap.usePlugin(plugins.Search)
  mmRef = new MindMap({
    el: document.getElementById('mindContainer_' + props.mapKey),
    mouseScaleCenterUseMousePosition: true,
    theme: 'default',
    data: props.data,
    readonly: props.readonly,
    layout: 'logicalStructure',
    iconList: iconList,
    /*watermarkConfig: {
      text: userStore.info.name + ' ' + userStore.info.code,
      lineSpacing: 80,
      textSpacing: 80,
      angle: 30,
      textStyle: {
        color: '#999',
        opacity: 0.3,
        fontSize: 14
      }
    }*/
  })
  mmRef.setThemeConfig({lineStyle: 'curve'})
  if (props.needLeft) {
    mmRef.view.translateX(-parseInt((initWidth.value / 2) + '') + 40)
  }
  window.addEventListener('resize', setMindMapResize)
  if(props.readonly)return
  setTimeout(() => {
    setMinMapInitEvent()
  }, 300)
})

const getWindowSize = () => {
  return {
    width: document.body.clientWidth,
    height: document.body.clientHeight,
  }
}

const setDisable = (item) => {
  const newItem = {...item}
  if (newItem.type === 1) {
    newItem.id = '1-' + newItem.id
    if (!newItem.children || newItem.children.length === 0) {
      newItem.disabled = true
    } else {
      newItem.children = newItem.children.map((child) => {
        return setDisable(child)
      })
    }
  } else {
    if (newItem.id === parseInt(route.params.mindmapId + '')) {
      newItem.disabled = true
    }
    newItem.id = '2-' + newItem.id
  }
  return newItem
}

const selectNodeTree = computed(() => {
  return noteStore.noteData[0].children.map((item) => {
    return setDisable(item)
  })
})

const setMinMapInitEvent = () => {
  mmRef.on('node_active', (node, activeNodeList) => {
    node && node.name
    activeNodes.value = activeNodeList
    closeSearchAndReplace()
  })
  mmRef.on('back_forward', (index, len) => {
    isStart.value = index <= 0
    isEnd.value = index >= len - 1
  })
  mmRef.on(
      'data_change',
      debounce((data) => {
        if (rootUpdate.value) {
          return
        }
        // emits('update:data', data)
        emits('change', data)
      }, 800)
  )
  mmRef.on('search_info_change', ({currentIndex, total}) => {
    searchIndex.value = currentIndex + 1
    searchTotal.value = total
  })
  mmRef.on('node_contextmenu', (e, node) => {
    const bodySize = getWindowSize()
    nodeMenu.node = node
    nodeMenu.top = e.clientY + 10
    nodeMenu.left = e.clientX + 10
    nodeMenu.isRoot = node.isRoot
    if (bodySize.height - nodeMenu.top < 330) {
      nodeMenu.top = bodySize.height - 330
    }
    if (bodySize.width - nodeMenu.left < 260) {
      nodeMenu.left = bodySize.width - 260
    }
    nodeMenu.show = true
  })
  mmRef.on('svg_mousedown', (e) => {
    if (e.which !== 3) {
      return
    }
    const bodySize = getWindowSize()
    bgMenu.top = e.clientY + 10
    bgMenu.left = e.clientX + 10
    if (bodySize.height - bgMenu.top < 200) {
      bgMenu.top = bodySize.height - 200
    }
    if (bodySize.width - bgMenu.left < 260) {
      bgMenu.left = bodySize.width - 260
    }
    bgMenu.show = true
  })
  mmRef.keyCommand.addShortcut('Control+f', () => {
    handleSearch()
  })
  mmRef.keyCommand.addShortcut('Control+r', () => {
    handleReplace()
  })
}

onUnmounted(() => {
  window.addEventListener('resize', setMindMapResize)
  mmRef.destroy()
  mmRef = null
})

const hasRoot = computed(() => {
  return (
      activeNodes.value.findIndex((node) => {
        return node.isRoot
      }) !== -1
  )
})

const setMindMapResize = () => {
  if (mmRef) {
    mmRef.resize()
  }
}

const handleTabor = (event) => {
  const eventUpper = (event + '').toUpperCase()
  if (event.startsWith('exec_')) {
    mmRef.execCommand(eventUpper.replace('EXEC_', ''))
  } else if (event.startsWith('insert_') || event.startsWith('handle_')) {
    const map = {
      insertImage,
      insertEmoji,
      insertLink,
      insertComment,
      insertOfficial,
      handleStyle,
      handleInit,
      handleExport,
      handleSearch,
      handleReplace,
      handleExec,
      handleLinkNote,
    }
    const key = camelize(event)
    map[key]()
  }
}

/**
 * 中划线转大驼峰
 * @param str
 * @returns {string}
 */
const camelize = (str) => {
  return (str + '').replace(/_\D/g, function (match) {
    return match.charAt(1).toUpperCase()
  })
}

const insertImage = (value = null) => {
  if (value === null) {
    imageVisible.value = true
  } else {
    activeNodes.value.forEach((node) => {
      node.setImage({
        url: value,
        title: '',
        width: 100, // 图片的宽高也不能少
        height: 100,
      })
    })
  }
}

const insertEmoji = (value = null) => {
  if (value === null) {
    emojiVisible.value = true
  } else {
    activeNodes.value.forEach((node) => {
      node.setIcon(value ? [value] : [])
    })
  }
}

const insertLink = (value = null) => {
  if (value === null) {
    const lastNodes = activeNodes.value[activeNodes.value.length - 1]
    const url = lastNodes.getData('hyperlink')
    const name = lastNodes.getData('hyperlinkTitle')
    if (isObjectJson(name)) {
      const obj = JSON.parse(name)
      insertNodeValue.value = '2-' + obj.id
      linkNoteVisible.value = true
    } else {
      linkData.url = url ? url : ''
      linkData.name = name ? name : ''
      linkVisible.value = true
    }
  } else {
    const {url, name} = value
    activeNodes.value.forEach((node) => {
      node.setHyperlink(url, name)
    })
  }
}

const confirmInsertNode = () => {
  if (!insertNodeValue.value) {
    activeNodes.value.forEach((node) => {
      node.setHyperlink('', '')
    })
  } else {
    const idArr = insertNodeValue.value.split('-')
    const noteId = parseInt(idArr[idArr.length - 1])
    const {label, icon} = getNoteNameAndIcon(selectNodeTree.value, insertNodeValue.value)
    const base = import.meta.env.VITE_BASE_PATH
    const filePath = getIconPath(icon)
    const url = getHost() + base + '/file/' + filePath + '/' + noteId
    activeNodes.value.forEach((node) => {
      const linkParams = {
        label,
        id: noteId,
        icon: icon
      }
      node.setHyperlink(url, JSON.stringify(linkParams))
    })
  }
  linkNoteVisible.value = false
}

const insertComment = async (val = null) => {
  if (val === null) {
    let noteValue = ''
    activeNodes.value.forEach((node) => {
      noteValue = node.getData('note')
    })
    const prompt = await ElMessageBox.prompt('请输入备注内容', '提示', {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      inputValue: noteValue,
      inputErrorMessage: '输入备注...',
    }).catch(() => null)
    if (prompt === null) return
    const {value} = prompt
    await insertComment(value)
  } else {
    activeNodes.value.forEach((node) => {
      node.setNote(val)
    })
  }
}

const insertOfficial = (value = null) => {
  if (value === null) {
    officialVisible.value = true
  } else {
    mmRef.execCommand('INSERT_FORMULA', value)
  }
}

const handleInit = () => {
  if (mmRef) {
    mmRef.renderer.setRootNodeCenter()
  }
}

const handleStyle = (value = null) => {
  if (value === null) {
    let lastNode = null
    activeNodes.value.forEach((node) => {
      lastNode = node
    })
    Object.keys(defaultNoteStyle).forEach((key) => {
      noteStyle[key] = lastNode.getStyle(key)
    })
    styleVisible.value = true
  } else {
    const {styleKey, styleValue} = value
    activeNodes.value.forEach((node) => {
      node.setStyle(styleKey, styleValue)
    })
  }
}

const handleExport = (value = null) => {
  if (value === null) {
    exportVisible.value = true
  } else {
    const {type, name} = value
    if (type.toUpperCase() === 'SVG') {
      mmRef.export(
          'svg',
          true,
          name,
          false,
          `* {margin: 10px; padding: 10px; box-sizing: border-box;}`
      )
    } else {
      mmRef.export(type.toLowerCase(), true, name)
    }
  }
}

const closeSearchAndReplace = () => {
  replaceVisible.value = false
  searchVisible.value = false
  nodeMenu.show = false
  bgMenu.show = false
}

const handleSearch = (e = null, callback = null) => {
  const call = () => {
    mmRef.search.search(searchValue.value, () => {
      if (typeof callback === 'function') callback()
    })
  }
  if (!e) {
    replaceVisible.value = false
    searchVisible.value = true
    nodeMenu.show = false
    bgMenu.show = false
  } else if (e !== 'replace') {
    if (e.keyCode === 13) {
      e.preventDefault()
      call()
    }
  } else {
    call()
  }
}

const handleReplace = (e = null, all = false) => {
  const call = () => {
    handleSearch('replace', () => {
      if (all) {
        mmRef.search.replaceAll(replaceValue.value)
      } else {
        mmRef.search.replace(replaceValue.value, true)
      }
    })
  }
  if (!e) {
    replaceVisible.value = true
    searchVisible.value = false
    nodeMenu.show = false
    bgMenu.show = false
  } else if (e !== 'click') {
    if (e.keyCode === 13) {
      e.preventDefault()
      call()
    }
  } else {
    call()
  }
}

const handleExec = (key) => {
  const notExecKeys = [
    'insert_node',
    'insert_parent_node',
    'up_node',
    'down_node',
    'remove_node',
    'remove_current_node',
  ]
  if (notExecKeys.indexOf(key) >= 0 && nodeMenu.isRoot) {
    return
  }
  closeSearchAndReplace()
  key = key.toUpperCase()
  switch (key) {
    case 'COPY_NODE':
      mmRef.renderer.copy()
      break
    case 'CUT_NODE':
      mmRef.renderer.cut()
      break
    case 'PASTE_NODE':
      mmRef.renderer.paste()
      break
    case 'RETURN_CENTER':
      mmRef.renderer.setRootNodeCenter()
      break
    case 'FIT_CANVAS':
      mmRef.view.fit(() => {
      }, false, 0)
      break
    case 'LINK_NOTE':
      handleLinkNote()
      break
    default:
      mmRef.execCommand(key.toUpperCase())
  }
}

const handleLinkNote = () => {
  if (!activeNodes.value.length) return
  linkNoteVisible.value = true
  const lastActiveNodes = activeNodes.value[activeNodes.value.length - 1]
  const {hyperlinkTitle} = lastActiveNodes.nodeData.data
  if (hyperlinkTitle && isObjectJson(hyperlinkTitle)) {
    const obj = JSON.parse(hyperlinkTitle)
    insertNodeValue.value = '2-' + obj.id
  } else {
    insertNodeValue.value = ''
  }
  linkNoteVisible.value = true
  closeSearchAndReplace()
}

const copyToClipboard = async () => {
  closeSearchAndReplace()
  try {
    const png = await mmRef.export('png', false)
    const blob = await imgToDataUrl(png, true)
    if (navigator.clipboard) {
      const data = [new ClipboardItem({['image/png']: blob})]
      await navigator.clipboard.write(data)
    } else {
      ElMessage.error('您的环境不支持复制操作')
    }
    ElMessage.success('已复制图片到剪贴板')
  } catch (err) {
    console.log(err)
    ElMessage.error('复制图片到剪贴板失败')
  }
}

defineExpose({
  handleExport
})
</script>
<style lang="scss" scoped>
.el-notification {
  width: 430px;
}
</style>
<style lang="less">
.mindContainer {
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100%;

  * {
    padding: 0;
    margin: 0;
  }
}

.mind-box {
  position: relative;
  width: 100%;
  height: 100%;
}

.node-menu {
  background: var(--el-bg-color);
  box-shadow: var(--el-box-shadow);
  position: fixed;
  z-index: 2012;
  padding: 15px 0;
  border-radius: 6px;
  width: 250px;

  .item {
    padding: 0 15px;
    cursor: pointer;
    line-height: 2;
    font-size: 13px;
    color: var(--el-text-color-primary);

    .keyboard {
      color: var(--el-text-color-secondary);
    }

    &:hover {
      background: var(--el-bg-color-page);
    }

    &.danger {
      color: var(--el-color-danger);
    }

    &.disable {
      color: var(--el-text-color-secondary);
      cursor: default;
      background: var(--el-bg-color);
    }
  }
}
</style>
